/* tContainer_t -
 * template class, first parameter is a type named T, 
 * second parameter is a template class with two parameters named container - intended two handle stl vector/list containers
 *
 *
 */

#pragma once
#include <vector>
#include <list>
#include <algorithm>

using namespace std;


template <typename T,										// first parameter is a type
		  template <typename, typename> class Container>	// second parameter is a template
class tContainer_t{

	// iterator names for the class
	typedef typename Container<T*, allocator<T*>>::iterator iter_t;
	typedef typename Container<T*, allocator<T*>>::const_iterator const_iter_t;

public:
	//CTOR
	tContainer_t(){
	}
	// DTOR - use default
	//~tContainer_t(){}

	// return a pointer to the element at 'index' in the container
	// throws a string exception: "illegal index exception" for illegal index 
	// illegal index i:  ( i < 0 ) || ( i >= size of container)
	T*& operator[](unsigned int index){

		// exit safely for illegal index
		if (index >= container.size()){
			//throw exception
			throw ("illegal index exception");
		}
		// iterate over the 
		const_iter_t itr = container.begin();
		for (unsigned int i = 0; i < index; i++,itr++);
		
		return (T*)*itr;
	}

	// return true iff the container is empty.
	bool empty() const {
		return container.empty();
	}

	// return the number of elements in the container.
	unsigned int size() const{
		return container.size();
	}

	// add a new element to the back of the container.
	void push_back(T* element){
		container.push_back(element);
	}

	// return the first element in the container.
	T* front() const{
		return container.front();
	}

	// return the last element in the container.
	T* back() const{
		return container.back();
	}

	// find element by value.
	// return pointer to the element if found, return 0 otherwise
	T* find(const T value) const {
		const_iter_t iter = find_if(container.begin(), container.end(), predicate(value));
		if (iter == container.end()){
			return 0;
		}
		return *iter;
	}

	// remove an element from the container and return it's pointer.
	// return pointer to the element if found, return 0 otherwise
	T* removeAndRetrieve(T value) {
		for (iter_t itr = container.begin(); itr != container.end(); itr++){
			if (**itr == value) { 
				T* ptr = *itr;
				container.erase(itr);
				return ptr;
			}//end of if
		}//end of for

		//could not find element with the same value in container
		return 0;
	}

	// remove all elements from the container. this function does not destroy any elements
	void removeAllElements(){
		container.clear();
	}

	// remove and delete element from the container. this function releases mem. used by the element
	// return true iff the element was found and removed
	bool removeAndDelete(T value){
		for (iter_t itr = container.begin(); itr != container.end(); itr++){
			if (**itr == value) { 
				T* ptr = *itr;
				// remove from container and call destructor
				container.erase(itr);
				delete ptr;
				return true;
			}//end of if
		}//end of for

		//could not find element with the same value in container
		return false;
	}


protected:
	// we allow derived classed to handle the container data-member
	// we expect the container type to handle ptrs. to T and use the default allocator
	Container<T*, allocator<T*>> container;

private:

	// no copying allowed - CPY-DTOR and operator=() declared with no implementation - visibility private
	tContainer_t(const tContainer_t& tcontainer_t);
	const tContainer_t & operator=(const tContainer_t & tcontainer_t);

	// a predicate struct used for find_if
	struct predicate{
		predicate(const T& element) : objectToCompare(element){}
		bool operator()(const T* element) const { return (objectToCompare == *element); }
		T objectToCompare;
	};

};